/**
@author		Jong Min, Lee
@date		  02/09/2014
@brief		event template class

copyright(c) Tae Young Soft. All right reserved. 2014
*/
#pragma once

#include <list>
#include <algorithm>
#include <afxmt.h>
#include "Taurus_Util/Delegate.h"

template<class TSender>
class TEvent
{
protected:
  CCriticalSection m_lock;

  std::list<TBaseDelegate<TSender>*> m_handlers;

  DWORD attach(TBaseDelegate<TSender>* handler);
  void detach(TBaseDelegate<TSender>* handler);

public:

  ~TEvent();

  DWORD operator +=(TBaseDelegate<TSender>* handler);

  void operator -=(TBaseDelegate<TSender>* handler);

  void operator()(const TSender* sender, void* parameter);

  void call(const TSender* sender, void* parameter);
};

template<class TSender>
TEvent<TSender>::~TEvent()
{
  m_lock.Lock();

  m_handlers.clear();

  m_lock.Unlock();
}

template<class TSender>
DWORD TEvent<TSender>::operator+=(TBaseDelegate<TSender>* handler)
{
  return attach(handler);
}

template<class TSender>
void TEvent<TSender>::operator-=(TBaseDelegate<TSender>* handler)
{
  detach(handler);
}

template<class TSender>
void TEvent<TSender>::operator()(const TSender* sender, void* parameter)
{
  call(sender, parameter);
}

template<class TSender>
DWORD TEvent<TSender>::attach(TBaseDelegate<TSender>* handler)
{
  if(handler == nullptr) return ERROR_INVALID_PARAMETER;

  m_lock.Lock();

  DWORD dwError = ERROR_SUCCESS;

  auto pos = find(m_handlers.begin(), m_handlers.end(), handler);
  if(pos == m_handlers.end())
  {
    try
    {
      m_handlers.push_back(handler);
    }
    catch(...)
    {
      dwError = ERROR_NOT_ENOUGH_MEMORY;
    }
  }

  m_lock.Unlock();
  return dwError;
}

template<class TSender>
void TEvent<TSender>::detach(TBaseDelegate<TSender>* handler)
{
  m_lock.Lock();

  auto pos = find(m_handlers.begin(), m_handlers.end(), handler);
  if(pos != m_handlers.end())
    m_handlers.erase(pos);

  m_lock.Unlock();
}

template<class TSender>
void TEvent<TSender>::call(const TSender* sender, void* parameter)
{
  m_lock.Lock();

  for(auto pos = m_handlers.begin(), end = m_handlers.end(); pos != end; ++pos)
    (*pos)->call(sender, parameter);

  m_lock.Unlock();
}
